////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any
//     purpose is hereby granted without fee, provided that the above copyright
//     notice appear in all copies and that both that copyright notice and this
//     permission notice appear in supporting documentation.
// The author or Addison-Wesley Longman make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
#ifndef LOKI_MULTIMETHODS_INC_
#define LOKI_MULTIMETHODS_INC_

// $Id: MultiMethods.h 751 2006-10-17 19:50:37Z syntheticpp $


#include "Typelist.h"
#include "LokiTypeInfo.h"
#include "Functor.h"
#include "AssocVector.h"

////////////////////////////////////////////////////////////////////////////////
// IMPORTANT NOTE:
// The double dispatchers implemented below differ from the excerpts shown in
// the book - they are simpler while respecting the same interface.
////////////////////////////////////////////////////////////////////////////////

namespace Loki
{
////////////////////////////////////////////////////////////////////////////////
// class template InvocationTraits (helper)
// Helps implementing optional symmetry
////////////////////////////////////////////////////////////////////////////////

namespace Private
{
template <class SomeLhs, class SomeRhs,
         class Executor, typename ResultType>
struct InvocationTraits
{
	static ResultType
	DoDispatch(SomeLhs &lhs, SomeRhs &rhs,
	           Executor &exec, Int2Type<false>)
	{
		return exec.Fire(lhs, rhs);
	}
	static ResultType
	DoDispatch(SomeLhs &lhs, SomeRhs &rhs,
	           Executor &exec, Int2Type<true>)
	{
		return exec.Fire(rhs, lhs);
	}
};
}

////////////////////////////////////////////////////////////////////////////////
// class template StaticDispatcher
// Implements an automatic static double dispatcher based on two typelists
////////////////////////////////////////////////////////////////////////////////

template
<
class Executor,
      class BaseLhs,
      class TypesLhs,
      bool symmetric = true,
      class BaseRhs = BaseLhs,
      class TypesRhs = TypesLhs,
      typename ResultType = void
      >
class StaticDispatcher
{
	template <class SomeLhs>
	static ResultType DispatchRhs(SomeLhs &lhs, BaseRhs &rhs,
	                              Executor exec, NullType)
	{
		return exec.OnError(lhs, rhs);
	}

	template <class Head, class Tail, class SomeLhs>
	static ResultType DispatchRhs(SomeLhs &lhs, BaseRhs &rhs,
	                              Executor exec, Typelist<Head, Tail>)
	{
		if (Head *p2 = dynamic_cast<Head *>(&rhs))
		{
			Int2Type<(symmetric &&
			          int(TL::IndexOf<TypesRhs, Head>::value) <
			          int(TL::IndexOf<TypesLhs, SomeLhs>::value))> i2t;

			typedef Private::InvocationTraits<
			SomeLhs, Head, Executor, ResultType> CallTraits;

			return CallTraits::DoDispatch(lhs, *p2, exec, i2t);
		}
		return DispatchRhs(lhs, rhs, exec, Tail());
	}

	static ResultType DispatchLhs(BaseLhs &lhs, BaseRhs &rhs,
	                              Executor exec, NullType)
	{
		return exec.OnError(lhs, rhs);
	}

	template <class Head, class Tail>
	static ResultType DispatchLhs(BaseLhs &lhs, BaseRhs &rhs,
	                              Executor exec, Typelist<Head, Tail>)
	{
		if (Head *p1 = dynamic_cast<Head *>(&lhs))
		{
			return DispatchRhs(*p1, rhs, exec, TypesRhs());
		}
		return DispatchLhs(lhs, rhs, exec, Tail());
	}

public:
	static ResultType Go(BaseLhs &lhs, BaseRhs &rhs,
	                     Executor exec)
	{
		return DispatchLhs(lhs, rhs, exec, TypesLhs());
	}
};

////////////////////////////////////////////////////////////////////////////////
// class template BasicDispatcher
// Implements a logarithmic double dispatcher for functors (or functions)
// Doesn't offer automated casts or symmetry
////////////////////////////////////////////////////////////////////////////////

template
<
class BaseLhs,
      class BaseRhs = BaseLhs,
      typename ResultType = void,
      typename CallbackType = ResultType ( *)(BaseLhs &, BaseRhs &)
      >
class BasicDispatcher
{
	typedef std::pair<TypeInfo,TypeInfo> KeyType;
	typedef CallbackType MappedType;
	typedef AssocVector<KeyType, MappedType> MapType;
	MapType callbackMap_;

	void DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun);
	bool DoRemove(TypeInfo lhs, TypeInfo rhs);

public:
	template <class SomeLhs, class SomeRhs>
	void Add(CallbackType fun)
	{
		DoAdd(typeid(SomeLhs), typeid(SomeRhs), fun);
	}

	template <class SomeLhs, class SomeRhs>
	bool Remove()
	{
		return DoRemove(typeid(SomeLhs), typeid(SomeRhs));
	}

	ResultType Go(BaseLhs &lhs, BaseRhs &rhs);
};

// Non-inline to reduce compile time overhead...
template <class BaseLhs, class BaseRhs,
         typename ResultType, typename CallbackType>
void BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
::DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun)
{
	callbackMap_[KeyType(lhs, rhs)] = fun;
}

template <class BaseLhs, class BaseRhs,
         typename ResultType, typename CallbackType>
bool BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
::DoRemove(TypeInfo lhs, TypeInfo rhs)
{
	return callbackMap_.erase(KeyType(lhs, rhs)) == 1;
}

template <class BaseLhs, class BaseRhs,
         typename ResultType, typename CallbackType>
ResultType BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
::Go(BaseLhs &lhs, BaseRhs &rhs)
{
	typename MapType::key_type k(typeid(lhs),typeid(rhs));
	typename MapType::iterator i = callbackMap_.find(k);
	if (i == callbackMap_.end())
	{
		throw std::runtime_error("Function not found");
	}
	return (i->second)(lhs, rhs);
}

////////////////////////////////////////////////////////////////////////////////
// class template StaticCaster
// Implementation of the CastingPolicy used by FunctorDispatcher
////////////////////////////////////////////////////////////////////////////////

template <class To, class From>
struct StaticCaster
{
	static To &Cast(From &obj)
	{
		return static_cast<To &>(obj);
	}
};

////////////////////////////////////////////////////////////////////////////////
// class template DynamicCaster
// Implementation of the CastingPolicy used by FunctorDispatcher
////////////////////////////////////////////////////////////////////////////////

template <class To, class From>
struct DynamicCaster
{
	static To &Cast(From &obj)
	{
		return dynamic_cast<To &>(obj);
	}
};

////////////////////////////////////////////////////////////////////////////////
// class template Private::FnDispatcherHelper
// Implements trampolines and argument swapping used by FnDispatcher
////////////////////////////////////////////////////////////////////////////////

namespace Private
{
template <class BaseLhs, class BaseRhs,
         class SomeLhs, class SomeRhs,
         typename ResultType,
         class CastLhs, class CastRhs,
         ResultType (*Callback)(SomeLhs &, SomeRhs &)>
struct FnDispatcherHelper
{
	static ResultType Trampoline(BaseLhs &lhs, BaseRhs &rhs)
	{
		return Callback(CastLhs::Cast(lhs), CastRhs::Cast(rhs));
	}
	static ResultType TrampolineR(BaseRhs &rhs, BaseLhs &lhs)
	{
		return Trampoline(lhs, rhs);
	}
};
}

////////////////////////////////////////////////////////////////////////////////
// class template FnDispatcher
// Implements an automatic logarithmic double dispatcher for functions
// Features automated conversions
////////////////////////////////////////////////////////////////////////////////

template <class BaseLhs, class BaseRhs = BaseLhs,
         typename ResultType = void,
         template <class, class> class CastingPolicy = DynamicCaster,
         template <class, class, class, class>
         class DispatcherBackend = BasicDispatcher>
class FnDispatcher
{
	DispatcherBackend<BaseLhs, BaseRhs, ResultType,
	                  ResultType ( *)(BaseLhs &, BaseRhs &)> backEnd_;

public:
	template <class SomeLhs, class SomeRhs>
	void Add(ResultType (*pFun)(BaseLhs &, BaseRhs &))
	{
		return backEnd_.template Add<SomeLhs, SomeRhs>(pFun);
	}

	template <class SomeLhs, class SomeRhs,
	         ResultType (*callback)(SomeLhs &, SomeRhs &)>
	void Add()
	{
		typedef Private::FnDispatcherHelper<
		BaseLhs, BaseRhs,
		         SomeLhs, SomeRhs,
		         ResultType,
		         CastingPolicy<SomeLhs,BaseLhs>,
		         CastingPolicy<SomeRhs,BaseRhs>,
		         callback> Local;

		Add<SomeLhs, SomeRhs>(&Local::Trampoline);
	}

	template <class SomeLhs, class SomeRhs,
	         ResultType (*callback)(SomeLhs &, SomeRhs &),
	         bool symmetric>
	void Add(bool = true) // [gcc] dummy bool
	{
		typedef Private::FnDispatcherHelper<
		BaseLhs, BaseRhs,
		         SomeLhs, SomeRhs,
		         ResultType,
		         CastingPolicy<SomeLhs,BaseLhs>,
		         CastingPolicy<SomeRhs,BaseRhs>,
		         callback> Local;

		Add<SomeLhs, SomeRhs>(&Local::Trampoline);
		if (symmetric)
		{
			Add<SomeRhs, SomeLhs>(&Local::TrampolineR);
		}
	}

	template <class SomeLhs, class SomeRhs>
	void Remove()
	{
		backEnd_.template Remove<SomeLhs, SomeRhs>();
	}

	ResultType Go(BaseLhs &lhs, BaseRhs &rhs)
	{
		return backEnd_.Go(lhs, rhs);
	}
};

////////////////////////////////////////////////////////////////////////////////
// class template FunctorDispatcherAdaptor
// permits use of FunctorDispatcher under gcc.2.95.2/3
///////////////////////////////////////////////////////////////////////////////

namespace Private
{
template <class BaseLhs, class BaseRhs,
         class SomeLhs, class SomeRhs,
         typename ResultType,
         class CastLhs, class CastRhs,
         class Fun, bool SwapArgs>
class FunctorDispatcherHelper
{
	Fun fun_;
	ResultType Fire(BaseLhs &lhs, BaseRhs &rhs,Int2Type<false>)
	{
		return fun_(CastLhs::Cast(lhs), CastRhs::Cast(rhs));
	}
	ResultType Fire(BaseLhs &rhs, BaseRhs &lhs,Int2Type<true>)
	{
		return fun_(CastLhs::Cast(lhs), CastRhs::Cast(rhs));
	}
public:
	FunctorDispatcherHelper(const Fun &fun) : fun_(fun) {}

	ResultType operator()(BaseLhs &lhs, BaseRhs &rhs)
	{
		return Fire(lhs,rhs,Int2Type<SwapArgs>());
	}
};
}

////////////////////////////////////////////////////////////////////////////////
// class template FunctorDispatcher
// Implements a logarithmic double dispatcher for functors
// Features automated casting
////////////////////////////////////////////////////////////////////////////////

template <class BaseLhs, class BaseRhs = BaseLhs,
         typename ResultType = void,
         template <class, class> class CastingPolicy = DynamicCaster,
         template <class, class, class, class>
         class DispatcherBackend = BasicDispatcher>
class FunctorDispatcher
{
	typedef LOKI_TYPELIST_2(BaseLhs &, BaseRhs &) ArgsList;
	typedef Functor<ResultType, ArgsList, LOKI_DEFAULT_THREADING> FunctorType;

	DispatcherBackend<BaseLhs, BaseRhs, ResultType, FunctorType> backEnd_;

public:
	template <class SomeLhs, class SomeRhs, class Fun>
	void Add(const Fun &fun)
	{
		typedef Private::FunctorDispatcherHelper<
		BaseLhs, BaseRhs,
		         SomeLhs, SomeRhs,
		         ResultType,
		         CastingPolicy<SomeLhs, BaseLhs>,
		         CastingPolicy<SomeRhs, BaseRhs>,
		         Fun, false> Adapter;

		backEnd_.template Add<SomeLhs, SomeRhs>(FunctorType(Adapter(fun)));
	}
	template <class SomeLhs, class SomeRhs, bool symmetric, class Fun>
	void Add(const Fun &fun)
	{
		Add<SomeLhs,SomeRhs>(fun);

		if (symmetric)
		{
			// Note: symmetry only makes sense where BaseLhs==BaseRhs
			typedef Private::FunctorDispatcherHelper<
			BaseLhs, BaseLhs,
			         SomeLhs, SomeRhs,
			         ResultType,
			         CastingPolicy<SomeLhs, BaseLhs>,
			         CastingPolicy<SomeRhs, BaseLhs>,
			         Fun, true> AdapterR;

			backEnd_.template Add<SomeRhs, SomeLhs>(FunctorType(AdapterR(fun)));
		}
	}

	template <class SomeLhs, class SomeRhs>
	void Remove()
	{
		backEnd_.template Remove<SomeLhs, SomeRhs>();
	}

	ResultType Go(BaseLhs &lhs, BaseRhs &rhs)
	{
		return backEnd_.Go(lhs, rhs);
	}
};
} // namespace Loki



#endif // end file guardian

